<template>
  <div class="viewer-container">
    <div class="buttons">
      <button @click="loadModel('example.glb')">显示 GLB 模型</button>
      <button @click="loadModel('example.fbx')">显示 FBX 模型</button>
      <button @click="loadModel('example.obj')">显示 OBJ 模型</button>
    </div>

    <div class="main-content">
      <div ref="threeDContainer" class="three-d-canvas"></div>

      <div class="controls-panel">
        <label for="scale-slider">模型大小控制：</label>
        <input type="range" id="scale-slider" min="0.01" max="2" step="0.01" v-model="modelScale" />
        <span>{{ (modelScale * 100).toFixed(0) }}%</span>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue'
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

const threeDContainer = ref(null)
const modelScale = ref(1.0) // 绑定到滑动条的ref变量

let scene, camera, renderer, controls
let currentModel = null
let animationFrameId = null
let initialCameraZ = 0 // 存储初始相机位置

const initThree = () => {
  // 创建场景
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0xcccccc)

  // 创建相机
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
  camera.position.set(0, 0, 5)
  initialCameraZ = camera.position.z

  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setSize(threeDContainer.value.clientWidth, threeDContainer.value.clientHeight)
  threeDContainer.value.appendChild(renderer.domElement)

  // 添加灯光
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
  scene.add(ambientLight)
  const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
  directionalLight.position.set(5, 5, 5).normalize()
  scene.add(directionalLight)

  // 添加轨道控制器，允许旋转
  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.05
  controls.screenSpacePanning = false
  controls.minDistance = 2
  controls.maxDistance = 10
  controls.maxPolarAngle = Math.PI / 2

  // 渲染循环
  const animate = () => {
    animationFrameId = requestAnimationFrame(animate)
    controls.update()
    renderer.render(scene, camera)
  }
  animate()

  // 窗口大小变化时更新渲染器
  window.addEventListener('resize', onWindowResize, false)
}

const onWindowResize = () => {
  camera.aspect = threeDContainer.value.clientWidth / threeDContainer.value.clientHeight
  camera.updateProjectionMatrix()
  renderer.setSize(threeDContainer.value.clientWidth, threeDContainer.value.clientHeight)
}

const disposeObject = (obj) => {
  if (obj) {
    obj.children.forEach((child) => disposeObject(child))
    if (obj.geometry && obj.geometry.dispose) {
      obj.geometry.dispose()
    }
    if (obj.material) {
      if (Array.isArray(obj.material)) {
        obj.material.forEach((mat) => {
          if (mat.dispose) mat.dispose()
        })
      } else if (obj.material.dispose) {
        obj.material.dispose()
      }
    }
    if (obj.parent) {
      obj.parent.remove(obj)
    }
  }
}

const loadModel = (modelName) => {
  if (currentModel) {
    disposeObject(currentModel)
    currentModel = null
  }

  // 每次加载新模型时重置滑动条和缩放
  modelScale.value = 1.0

  const modelPath = `/models/${modelName}`
  let loader
  const extension = modelName.split('.').pop().toLowerCase()

  switch (extension) {
    case 'glb':
    case 'gltf':
      loader = new GLTFLoader()
      break
    case 'fbx':
      loader = new FBXLoader()
      break
    case 'obj':
      loader = new OBJLoader()
      break
    default:
      console.error('不支持的文件类型')
      return
  }

  loader.load(
    modelPath,
    (object) => {
      const model = object.scene || object
      currentModel = model

      const box = new THREE.Box3().setFromObject(model)
      const center = box.getCenter(new THREE.Vector3())
      const size = box.getSize(new THREE.Vector3())

      model.position.sub(center)

      const maxDim = Math.max(size.x, size.y, size.z)
      const fov = camera.fov * (Math.PI / 180)
      let cameraZ = Math.abs(maxDim / 2 / Math.tan(fov / 2))
      cameraZ *= 1.5

      camera.position.z = cameraZ
      camera.updateProjectionMatrix()
      initialCameraZ = camera.position.z

      controls.target.copy(center)
      controls.update()

      scene.add(model)
      console.log(`${modelName} 加载成功`)
    },
    (xhr) => {
      console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
    },
    (error) => {
      console.error(`加载 ${modelName} 失败`, error)
    },
  )
}

// 监听 modelScale 变量的变化
watch(modelScale, (newScale) => {
  if (currentModel) {
    // 应用新的缩放值
    currentModel.scale.set(newScale, newScale, newScale)

    // 调整相机位置，防止模型变大后超出视野
    camera.position.z = initialCameraZ / newScale
    camera.updateProjectionMatrix()
    controls.update()
  }
})

onMounted(() => {
  if (threeDContainer.value) {
    initThree()
    loadModel('scene.glb')
  }
})

onUnmounted(() => {
  window.removeEventListener('resize', onWindowResize, false)
  cancelAnimationFrame(animationFrameId)
  if (renderer) renderer.dispose()
  if (currentModel) disposeObject(currentModel)
})
</script>

<style scoped>
.viewer-container {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: #f0f0f0;
}
.buttons {
  margin-bottom: 20px;
}
.buttons button {
  margin: 0 10px;
  padding: 10px 20px;
  font-size: 16px;
  cursor: pointer;
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 5px;
}
.buttons button:hover {
  background-color: #368f6d;
}

.main-content {
  display: flex;
  width: 90%;
  height: 80%;
}

.three-d-canvas {
  flex-grow: 1; /* 占据剩余空间 */
  border: 1px solid #ccc;
  background-color: white;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.controls-panel {
  width: 250px;
  padding: 20px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  margin-left: 20px;
  border: 1px solid #ccc;
  border-radius: 5px;
  background-color: white;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

#scale-slider {
  width: 100%;
  margin-top: 10px;
}
</style>
